1 00:00:00,910 --> 00:00:06,520 Alrighty, in this lecture, we're now going to move on to scripting the AI for our zombies. 2 00:00:06,520 --> 00:00:11,710 We're going to be implementing the pathfinding service in order to create a zombie that can path, find, 3 00:00:11,710 --> 00:00:14,800 and chase after the closest available player. 4 00:00:15,130 --> 00:00:19,630 Now, before we start scripting our zombies, we want to do the same thing that we did for our guns 5 00:00:19,630 --> 00:00:24,490 and create a module script that's going to store all of the properties for each of our zombies. 6 00:00:24,490 --> 00:00:28,870 We want to be able to customize the health of our zombie, how fast our zombie moves when chasing a 7 00:00:28,870 --> 00:00:34,390 player, whether our zombie regenerates health or not, how much damage the zombie deals, and so on. 8 00:00:34,390 --> 00:00:39,160 So if we head to server storage and we go to the zombies folder, we have a bunch of different zombies 9 00:00:39,160 --> 00:00:41,290 that I'm going to be planning to add into my game. 10 00:00:41,290 --> 00:00:47,170 For now, let's just move the basic original zombie into our workspace, and I'll just place them in 11 00:00:47,170 --> 00:00:48,370 my zombies folder. 12 00:00:49,160 --> 00:00:51,110 And here's my zombie right here. 13 00:00:51,890 --> 00:00:53,540 And inside of him there's not much. 14 00:00:53,540 --> 00:00:55,310 It's just a humanoid with some limbs. 15 00:00:55,310 --> 00:01:01,040 This is just basically a AR 15 rig with the colors changed and a different face added onto it. 16 00:01:01,040 --> 00:01:06,320 But we do have our hulas damaged object value in here, which we're going to be using to keep track 17 00:01:06,320 --> 00:01:08,030 of who killed a zombie. 18 00:01:08,270 --> 00:01:12,380 And other than that, this is just a basic humanoid setup for our zombie. 19 00:01:12,380 --> 00:01:17,300 So that means we need to script all of the different functionality for actually moving our zombie around 20 00:01:17,300 --> 00:01:18,920 and chasing our players. 21 00:01:18,920 --> 00:01:23,630 But as I said before we do that, let's go ahead and create a new module script inside of our zombie 22 00:01:23,630 --> 00:01:26,360 that's going to be storing all of the properties for it. 23 00:01:27,130 --> 00:01:31,780 Inside of here, I'll just create a table called self, and I'll make sure to return that at the end 24 00:01:31,780 --> 00:01:33,130 of this module script. 25 00:01:33,130 --> 00:01:37,900 And inside of here we can go ahead and define a whole bunch of different things for our AI. 26 00:01:37,930 --> 00:01:42,940 One of the first things I want to go ahead and define are the different properties we want to manipulate 27 00:01:42,940 --> 00:01:47,500 for the zombies humanoid, such as the health and how far the zombie can jump. 28 00:01:47,500 --> 00:01:51,580 So I'm going to create a key here, and I'm going to call it humanoid stats. 29 00:01:51,580 --> 00:01:53,620 And I'm going to set that equal to a table. 30 00:01:53,620 --> 00:01:57,880 And inside of here we can go ahead and store a bunch of key value pairs for different properties in 31 00:01:57,880 --> 00:02:03,160 the humanoid, such as the max health, the health, the jump power, things like that. 32 00:02:03,160 --> 00:02:10,180 So for the max health for this zombie, I'm going to set it to 200 health points. 33 00:02:10,180 --> 00:02:15,130 And then for the health or the starting health of the zombie, I'm also going to set it at 200 health 34 00:02:15,130 --> 00:02:15,850 points. 35 00:02:16,210 --> 00:02:23,440 And then for the jump power, I'm going to set the jump power equal to 50, which is the default. 36 00:02:23,950 --> 00:02:28,600 Now some other things I want to go ahead and define are the different speeds for my zombie. 37 00:02:28,600 --> 00:02:33,040 For example, I want to define a walk speed for when my zombie is just wandering around. 38 00:02:33,040 --> 00:02:37,660 And I also want to walk speed for my zombies chasing after a player so we can have two speeds. 39 00:02:37,660 --> 00:02:44,620 One we'll call the chase speed, and then we'll have another one called the Wander Speed. 40 00:02:45,490 --> 00:02:47,560 Now for the chasing speed. 41 00:02:47,560 --> 00:02:47,800 Oops. 42 00:02:47,800 --> 00:02:50,230 Let me put a comma here for the chasing speed. 43 00:02:50,230 --> 00:02:54,880 I want it to be a little bit slower than the player because this is just our regular zombie. 44 00:02:54,880 --> 00:02:57,670 It's not like our fast zombies that are going to be able to run. 45 00:02:57,670 --> 00:03:00,640 So I'll set the chasing speed to a value of like 14. 46 00:03:00,640 --> 00:03:05,650 And then for the wandering speed, I'm going to set it to a low value like five. 47 00:03:06,640 --> 00:03:12,430 Next up, we want to be able to add regeneration to our zombies if we so choose. 48 00:03:12,460 --> 00:03:17,950 So for this particular zombie, yes, I would like the zombie to regenerate health once the health of 49 00:03:17,950 --> 00:03:20,740 the zombie goes down a specific threshold. 50 00:03:20,740 --> 00:03:26,140 So to define that we can create a boolean we'll call it regenerates health. 51 00:03:26,140 --> 00:03:28,030 And I'm going to set that equal to true. 52 00:03:28,030 --> 00:03:33,700 And then we need to define how much health we should generate and how often we should regenerate that 53 00:03:33,700 --> 00:03:34,420 health. 54 00:03:35,100 --> 00:03:38,160 So I'll call this my health regen amount. 55 00:03:38,160 --> 00:03:40,170 And we could do like five health points. 56 00:03:40,170 --> 00:03:44,190 And we want to regenerate five health points every let's say second. 57 00:03:44,190 --> 00:03:46,500 So health regen tick. 58 00:03:46,500 --> 00:03:48,810 We can call this it'll be one. 59 00:03:48,810 --> 00:03:52,110 So we'll regenerate five health points every single second. 60 00:03:52,410 --> 00:03:56,670 However I also want to create a limit for this health regeneration. 61 00:03:56,670 --> 00:03:59,880 So I'll call this my health regen limit. 62 00:03:59,880 --> 00:04:02,430 And I'm going to set it to a value of 0.5. 63 00:04:02,430 --> 00:04:08,640 Which means once my zombie reaches at least half of its health, I'm not going to regenerate health 64 00:04:08,640 --> 00:04:09,450 anymore. 65 00:04:09,450 --> 00:04:14,400 So if the zombies health gets really low, let's say it gets to like ten a health points, then yes, 66 00:04:14,400 --> 00:04:19,140 I would like the zombie to regenerate health until it gets half of its health back, and then once it 67 00:04:19,140 --> 00:04:22,410 gets half of its health back, we're not going to regenerate the health anymore. 68 00:04:22,410 --> 00:04:28,260 We also don't want to regenerate any health until the health goes below that halfway mark. 69 00:04:28,260 --> 00:04:32,520 So this is going to be our limit for regenerating health. 70 00:04:33,150 --> 00:04:38,370 The next thing we want to go ahead and define is the targeting distance for our zombie. 71 00:04:38,370 --> 00:04:43,440 Basically, how far away can a zombie detect a player's presence. 72 00:04:43,440 --> 00:04:47,190 So we'll call this our targeting distance. 73 00:04:47,190 --> 00:04:51,300 And I'm going to set it to quite a high value of 150 studs. 74 00:04:51,660 --> 00:04:56,910 And then I want to have another targeting distance where the zombie is more alert. 75 00:04:56,910 --> 00:05:02,280 So basically when the zombies wandering around, it's going to have this 150 stud targeting distance. 76 00:05:02,280 --> 00:05:07,950 Now if a player's outside of that targeting distance, but the player shoots and hurts the zombie, 77 00:05:07,950 --> 00:05:14,070 well, I want the zombie to then become more alert and look a little bit farther away so we could have 78 00:05:14,070 --> 00:05:18,210 another targeting distance called alert targeting distance. 79 00:05:18,830 --> 00:05:23,480 And we can set this one to a little bit of a higher value, like 190 studs. 80 00:05:23,660 --> 00:05:31,100 Now this next property I'm going to define here, I'm going to call it recalculate path after distance. 81 00:05:31,100 --> 00:05:34,190 And I'm going to set this to a value of 20 studs. 82 00:05:34,190 --> 00:05:35,390 What is this for. 83 00:05:35,420 --> 00:05:41,780 Well what this is going to be for is when our zombie is pathfinding towards a particular player, we're 84 00:05:41,780 --> 00:05:47,570 going to check if our zombie is able to see that player using a raycast. 85 00:05:47,570 --> 00:05:52,130 If the zombie can see the player, then there's no point pathfinding. 86 00:05:52,130 --> 00:05:55,370 Instead, we can have the zombie run directly towards the player. 87 00:05:55,370 --> 00:06:02,900 However, if the player is pathfinding along a set of waypoints and they get 20 studs away from the 88 00:06:02,900 --> 00:06:07,820 first waypoint that they path found towards, well, the zombie is going to be like, okay, I've been 89 00:06:07,820 --> 00:06:13,970 pathfinding for more than 20 studs and I still haven't seen this player that I'm trying to go after. 90 00:06:13,970 --> 00:06:18,860 So instead, what the zombie is going to do is it's going to stop pathfinding, it's going to recap 91 00:06:19,220 --> 00:06:23,090 out the path and instead grab a new closest player. 92 00:06:23,090 --> 00:06:28,430 If the new closest player is the same player we were originally pathfinding after then fine, it'll 93 00:06:28,430 --> 00:06:34,370 continue to pathfind after that player, but if it finds a new player to Pathfind towards, then it's 94 00:06:34,370 --> 00:06:37,010 going to Pathfind towards that other player. 95 00:06:37,160 --> 00:06:43,760 So this basically just sets a value of how long we should pathfind for, until we want to recalculate 96 00:06:43,760 --> 00:06:47,300 the pathfinding to see if we can find a new closest player. 97 00:06:47,940 --> 00:06:52,620 Now, the next property I want to go ahead and define is how much a damage I want my zombie to deal 98 00:06:52,620 --> 00:06:53,820 when it attacks a player. 99 00:06:53,820 --> 00:06:56,730 So we could call this damage per hit. 100 00:06:56,730 --> 00:07:00,660 And I'm going to set it to ten health points because this is just our regular zombie. 101 00:07:00,660 --> 00:07:02,670 He's going to be relatively weak. 102 00:07:02,670 --> 00:07:07,320 And then we want to add a cooldown or how often this zombie can attack our player. 103 00:07:07,320 --> 00:07:11,400 So we'll call this cooldown between attack. 104 00:07:11,700 --> 00:07:17,730 And since this is going to be the base zombie, I'm going to set it to a value of like 3.5 seconds. 105 00:07:17,730 --> 00:07:23,100 So this means our zombie can deal ten damage every 3.5 seconds. 106 00:07:23,640 --> 00:07:27,510 We also want to be able to define the attack range for a zombie. 107 00:07:27,510 --> 00:07:32,220 So how close does our player have to be in order for our zombie to deal this damage? 108 00:07:32,220 --> 00:07:34,710 And I'm going to set it to a value of three studs. 109 00:07:36,050 --> 00:07:42,140 And then this next property I'm going to define, I'm going to call it the max visibility height. 110 00:07:42,140 --> 00:07:48,230 And this parameter is going to tell our AI how high up a player can be before. 111 00:07:48,230 --> 00:07:50,750 We don't want to pathfind after them any longer. 112 00:07:50,750 --> 00:07:55,310 So for example, let's say my zombie was at the ground level. 113 00:07:55,830 --> 00:08:02,850 And yet my player was like, high up in the sky somewhere on some kind of pillar or something like that. 114 00:08:02,880 --> 00:08:08,820 Well, clearly my zombie is not going to see that player because they're so high up and it's not going 115 00:08:08,820 --> 00:08:12,510 to be worth the time to try and pathfind towards that player. 116 00:08:12,510 --> 00:08:19,620 So we want to set a threshold for the max height the zombie can pathfind towards a player. 117 00:08:19,620 --> 00:08:25,350 So we're going to be comparing the position on the y axis for a zombie compared to the position on the 118 00:08:25,350 --> 00:08:29,400 y axis for a player who's high up standing on something. 119 00:08:29,400 --> 00:08:35,550 And I found a value of eight studs to be a good middle ground for this property, of course, you're 120 00:08:35,550 --> 00:08:39,270 going to be able to tweak all of these values to your preference, but for now, I'm just going to keep 121 00:08:39,270 --> 00:08:40,860 it at the value of eight. 122 00:08:41,250 --> 00:08:46,650 The next thing I want to go ahead and define is how much money we should give to a player that kills 123 00:08:46,650 --> 00:08:51,810 this zombie, which is why this who last damaged object value is super important because when our zombie 124 00:08:51,810 --> 00:08:57,750 dies, we want to find out who last damaged the zombie and then award them some money when the zombie 125 00:08:57,750 --> 00:08:58,290 dies. 126 00:08:58,290 --> 00:09:03,750 So we'll call this property our money drop, and I'm going to set it equal to a table with a minimum 127 00:09:03,750 --> 00:09:06,060 and a maximum value. 128 00:09:06,060 --> 00:09:10,770 So we're going to randomly give the player money between these two values for the minimum. 129 00:09:10,770 --> 00:09:12,420 I'm just going to set it to like $5. 130 00:09:12,420 --> 00:09:14,970 And then for the max I'm going to set it to $10. 131 00:09:14,970 --> 00:09:16,710 This is because it's just the base zombie. 132 00:09:16,710 --> 00:09:21,180 We don't want to give players that much money for killing the base zombie. 133 00:09:21,750 --> 00:09:25,470 Next, we want to go ahead and define a section for some animations. 134 00:09:25,470 --> 00:09:31,650 For zombie specifically, we want to be able to have a table to store all of the different attack animation 135 00:09:31,650 --> 00:09:33,900 IDs for our zombie. 136 00:09:33,900 --> 00:09:39,000 So I want to put in like a couple different attack animations the zombie can have, and then we're just 137 00:09:39,000 --> 00:09:43,770 going to randomly pick one of those attack animations and play it on our zombie. 138 00:09:43,770 --> 00:09:48,330 So we'd have an animation there and another animation here, put the IDs in there. 139 00:09:48,330 --> 00:09:50,850 And that's going to be our attack animations. 140 00:09:50,910 --> 00:09:56,460 Now we don't need to define other animations in here, like the walking or the running animations. 141 00:09:56,460 --> 00:09:59,340 We're going to be doing that later, so don't worry about those for now. 142 00:09:59,940 --> 00:10:05,610 However, the next section inside of this module script I want to go ahead and define is the agent parameters 143 00:10:05,610 --> 00:10:07,680 for the pathfinding service. 144 00:10:07,680 --> 00:10:14,310 So if you remember back in the pathfinding service lecture we had, I talked about the different agent 145 00:10:14,310 --> 00:10:18,300 parameters for creating a path from the pathfinding service. 146 00:10:18,300 --> 00:10:23,880 So we need to define some agent parameters for the agent radius. 147 00:10:23,880 --> 00:10:31,260 That's going to be equal to one stud for the zombie, which is half of the width of the humanoid route 148 00:10:31,260 --> 00:10:31,860 part. 149 00:10:32,040 --> 00:10:39,450 The agent height is going to be about five studs, because that is about the same value of the height 150 00:10:39,450 --> 00:10:40,950 for our zombie. 151 00:10:41,910 --> 00:10:43,320 The agent can jump. 152 00:10:43,320 --> 00:10:45,000 We're going to set equal to true. 153 00:10:45,660 --> 00:10:46,500 We're going to set. 154 00:10:46,500 --> 00:10:50,970 Agent can climb equal to false because there's no trust parts in my map. 155 00:10:50,970 --> 00:10:54,540 So I'm not worried about my zombie needing to climb up anything. 156 00:10:54,570 --> 00:10:58,470 We're going to set the waypoint spacing equal to a value of four. 157 00:10:58,500 --> 00:11:03,270 That's a good middle ground for the detailed ness of our paths. 158 00:11:04,250 --> 00:11:07,940 And then we don't need to define any costs for this zombie. 159 00:11:07,940 --> 00:11:12,650 But of course, if you would like to define some costs, you can totally do so I'm just going to leave 160 00:11:12,650 --> 00:11:18,290 this table blank because I don't have any materials in mind that the zombie needs to avoid. 161 00:11:19,070 --> 00:11:23,450 Otherwise, this last section we can go ahead and define is going to be all the different sounds for 162 00:11:23,450 --> 00:11:24,110 our zombie. 163 00:11:24,110 --> 00:11:26,810 So I want to randomly play different sounds. 164 00:11:26,810 --> 00:11:34,280 For example, when the zombie is wandering around, we'll just play some regular groaning zombie noises. 165 00:11:34,280 --> 00:11:38,780 When the zombie sees a player, then we want to play like a different sound where the zombies like, 166 00:11:38,780 --> 00:11:41,300 oh shoot, I see somebody, I got to chase after them. 167 00:11:41,300 --> 00:11:44,570 We want to have a section to define different death sounds. 168 00:11:44,570 --> 00:11:49,400 So when a player kills a zombie, the zombie will be like, ah, and then we'll play that. 169 00:11:49,400 --> 00:11:53,180 And then we also want to have some different sounds for when the zombie is attacking our player. 170 00:11:53,180 --> 00:11:57,740 So when the zombie is swinging their arms, we want to play another sound for that. 171 00:11:58,070 --> 00:12:02,900 So we could have a table to store different attack sound IDs. 172 00:12:03,570 --> 00:12:07,710 We can have a table to store different death sound IDs. 173 00:12:08,460 --> 00:12:15,720 We could have a table to define the different audio sounds for when the zombie sees a player, so we'll 174 00:12:15,720 --> 00:12:19,710 call this player scene sound IDs. 175 00:12:20,500 --> 00:12:25,810 And then we can also define the different growling sound IDs for when the zombie is wandering around. 176 00:12:25,810 --> 00:12:28,360 So we'll call it growl sound IDs. 177 00:12:28,360 --> 00:12:35,050 And inside of here is where we can go to the toolbox, find some audio that would kind of match these 178 00:12:35,050 --> 00:12:38,950 different tables, and then fill their IDs into them. 179 00:12:40,010 --> 00:12:46,160 Okay, so after scouring the toolbox for a little while, I found a whole bunch of sound IDs that I'm 180 00:12:46,160 --> 00:12:47,960 going to be using for my attack sounds. 181 00:12:47,960 --> 00:12:52,730 My death sounds when the player is seen and when the zombie is growling. 182 00:12:52,730 --> 00:12:57,860 If you would like to use the exact same audio sounds that I'm going to be using, then I'll have this 183 00:12:57,860 --> 00:13:03,320 chunk of text attached to the lecture so you can go ahead and copy and paste it into the properties 184 00:13:03,320 --> 00:13:05,360 module script for your zombies. 185 00:13:05,360 --> 00:13:10,580 Otherwise, the last thing we can go ahead and define are going to be the sound properties for these 186 00:13:10,580 --> 00:13:11,570 sound IDs. 187 00:13:11,570 --> 00:13:15,530 So we'll basically just have a general sound properties to apply for. 188 00:13:15,530 --> 00:13:17,450 When we play the sound IDs. 189 00:13:17,450 --> 00:13:23,000 For example, the volume will set to like 0.5 the roll off max distance. 190 00:13:23,000 --> 00:13:25,550 We could do a distance of like 45 studs. 191 00:13:25,550 --> 00:13:29,510 The roll off minimum distance we'll do like 15 studs. 192 00:13:29,810 --> 00:13:35,720 And then of course the roll off mode we're going to set to the enum dot roll off mode of inverse taper. 193 00:13:36,460 --> 00:13:42,400 And this should be all of the properties that we're going to need for our zombie. 194 00:13:43,180 --> 00:13:43,720 Okay. 195 00:13:43,720 --> 00:13:49,090 So now what we're going to do is we're going to create a new server script inside of our zombie. 196 00:13:49,090 --> 00:13:52,510 And this is going to be our zombie AI script. 197 00:13:52,510 --> 00:13:55,180 So we'll just call it, uh, zombie AI. 198 00:13:56,330 --> 00:13:57,080 Inside of the script. 199 00:13:57,080 --> 00:14:00,710 We're going to be using that same comment template that we've always been using. 200 00:14:00,800 --> 00:14:03,200 We're going to get rid of that section, get rid of that section. 201 00:14:03,200 --> 00:14:08,570 And one of the first things we're going to need inside of this server script is obviously the path finding 202 00:14:08,570 --> 00:14:09,380 service. 203 00:14:09,860 --> 00:14:16,070 So we'll create a variable for the path finding service game get service path finding service. 204 00:14:16,100 --> 00:14:18,290 We're also going to need the player service. 205 00:14:18,290 --> 00:14:23,060 That way we can go ahead and look through all of the different characters that are on the map and grab 206 00:14:23,060 --> 00:14:25,850 the closest one to our zombie. 207 00:14:26,990 --> 00:14:31,940 Now, of course, we're going to need access to the properties of our zombie, so we'll require. 208 00:14:32,030 --> 00:14:38,330 We'll get our zombie, which is script dot parent, and we'll get the properties for our zombie. 209 00:14:38,330 --> 00:14:40,880 And actually, let's go ahead and make a variable for our zombie. 210 00:14:40,880 --> 00:14:44,900 We'll just do script dot parent and then we can pass zombie here. 211 00:14:45,170 --> 00:14:50,540 Because now what we also need is we're going to need the humanoid for our zombie to be able to move 212 00:14:50,540 --> 00:14:51,230 our zombie around. 213 00:14:51,230 --> 00:14:53,180 So that's equal to zombie dot humanoid. 214 00:14:53,180 --> 00:14:58,400 We're going to need the root part of the zombie as well, so I'll call it my root part. 215 00:14:58,520 --> 00:15:00,290 That's equal to zombie dot. 216 00:15:00,290 --> 00:15:01,640 Humanoid root part. 217 00:15:02,570 --> 00:15:08,480 And then we're also going to need the head of our zombie, and we're going to be using the zombies heads 218 00:15:08,480 --> 00:15:16,460 position to raycast to see if the zombie is able to, you know, see a player, and if the zombie sees 219 00:15:16,460 --> 00:15:24,290 a player, then we can directly chase after that player and the head is equal to our zombie dot head. 220 00:15:25,690 --> 00:15:28,030 Now all we can go ahead and do is in the main section. 221 00:15:28,030 --> 00:15:29,800 We're going to create a while loop. 222 00:15:29,800 --> 00:15:33,460 And this while loop is going to execute all of the functionality for our AI. 223 00:15:33,490 --> 00:15:38,860 So the while loop is going to continuously check for any players that are nearby to our zombie. 224 00:15:38,860 --> 00:15:42,370 And then if we can see those players, we're going to chase directly after them. 225 00:15:42,370 --> 00:15:44,590 Otherwise we'll pathfind towards those players. 226 00:15:44,590 --> 00:15:49,450 And if we cannot find any players close by to us, then instead we're going to have the zombie wander 227 00:15:49,450 --> 00:15:50,410 around the map. 228 00:15:50,410 --> 00:15:54,910 So I only want this while loop to execute while our zombie is alive. 229 00:15:54,910 --> 00:15:56,590 So let's go ahead and create a variable. 230 00:15:56,590 --> 00:15:59,800 I'll call it alive and set it equal to true by default. 231 00:15:59,800 --> 00:16:02,590 And we're going to use this as the condition for our while loop. 232 00:16:02,590 --> 00:16:06,190 That way we're going to only execute this code when the zombie is alive. 233 00:16:06,190 --> 00:16:10,690 And then we can have a function listen to when our zombie dies, and then update the value inside of 234 00:16:10,690 --> 00:16:14,170 this variable to false to stop the while loop from executing. 235 00:16:14,750 --> 00:16:20,000 Now in order to see what players are closest to our zombie, we could go ahead and create a function 236 00:16:20,240 --> 00:16:22,970 and I'll call this function get nearest player. 237 00:16:24,840 --> 00:16:29,580 And we need to loop through all of the players in the game and make sure that these players are alive. 238 00:16:29,580 --> 00:16:34,080 They're not on the dead team, and that they are close enough to our zombie for our zombie to be able 239 00:16:34,080 --> 00:16:35,640 to pathfind to them. 240 00:16:35,640 --> 00:16:40,590 So we would loop through every single player and players get players. 241 00:16:40,920 --> 00:16:45,720 And what we want to do is we want to first make sure that this player is on the alive team. 242 00:16:45,720 --> 00:16:51,630 So if this player's team is equal to the dead team, then we don't want to pathfind towards this player. 243 00:16:51,630 --> 00:16:54,990 We're going to ignore them and move on to the next player in this loop. 244 00:16:54,990 --> 00:16:57,420 So let's go ahead and grab the team service. 245 00:17:00,040 --> 00:17:07,390 And then we can check if the player team is equal to the team of dead. 246 00:17:08,120 --> 00:17:14,090 If they're equal to the team of dead, or this player does not have a character, or let's say this 247 00:17:14,090 --> 00:17:20,540 player has a character, but the health of their humanoid is less than or equal to zero, then we're 248 00:17:20,540 --> 00:17:23,330 just going to continue to the next iteration of the loop. 249 00:17:24,640 --> 00:17:29,860 Otherwise, what we can go ahead and do is we can go ahead and calculate the distance between our zombie 250 00:17:29,860 --> 00:17:30,850 and this player. 251 00:17:30,850 --> 00:17:36,790 So we're going to set distance equal to my root part dot position. 252 00:17:37,150 --> 00:17:40,180 Subtract this by the humanoid root part of our player. 253 00:17:40,180 --> 00:17:43,420 So we'll create another variable I'll call this new target. 254 00:17:43,600 --> 00:17:47,740 And we'll set that equal to player dot character dot humanoid root part. 255 00:17:47,740 --> 00:17:51,040 And we're going to subtract by the position of our new target. 256 00:17:52,090 --> 00:17:56,170 And then we can go ahead and get the magnitude between these two positions. 257 00:17:56,200 --> 00:18:03,670 Now, if this distance is less than or equal to our properties dot targeting distance, then we can 258 00:18:03,670 --> 00:18:07,330 go ahead and say, yep, this player is close enough to be our target. 259 00:18:07,330 --> 00:18:13,180 So we can go ahead and create a variable up here I'm going to call it closest target. 260 00:18:13,300 --> 00:18:14,830 Set it equal to nil for now. 261 00:18:14,830 --> 00:18:18,460 And then we would set closest target equal to this new target. 262 00:18:18,460 --> 00:18:22,030 And then we would return closest target at the end of our for loop. 263 00:18:22,030 --> 00:18:24,970 Now there is a little bit of a problem with this situation. 264 00:18:24,970 --> 00:18:29,320 And that's what if there are multiple players within range of our zombie. 265 00:18:29,320 --> 00:18:36,100 Well whatever player last is within range is going to be set as the closest target and they may not 266 00:18:36,100 --> 00:18:38,020 actually be the closest target. 267 00:18:38,020 --> 00:18:43,300 So another thing we need to compare is the last distance of the player compared to another player. 268 00:18:43,980 --> 00:18:48,990 So what I'm going to do is I'm going to create another variable and I'm going to call this last distance. 269 00:18:48,990 --> 00:18:53,220 And by default I'm just going to set it to a very high value like 10,000. 270 00:18:53,220 --> 00:18:58,680 So that way we can compare the first player in the iteration of this loop to this last distance. 271 00:18:59,100 --> 00:19:03,030 So if the distance is less than the targeting distance. 272 00:19:03,630 --> 00:19:08,160 And this distance is also less than the last distance. 273 00:19:08,250 --> 00:19:11,100 Then we can go ahead and set closest target to this new target. 274 00:19:11,100 --> 00:19:15,630 And then we can also update last distance equal to this new distance. 275 00:19:16,050 --> 00:19:22,380 So now we are guaranteed to only grab the player that is closest to our zombie. 276 00:19:22,710 --> 00:19:27,300 Another important thing to note is that I told you earlier that I would like to update the targeting 277 00:19:27,300 --> 00:19:28,590 distance of the zombie. 278 00:19:28,590 --> 00:19:34,860 If the zombie gets shot and is alerted, or the zombie is in the alerted state to go ahead and target 279 00:19:34,860 --> 00:19:36,420 players that are farther away. 280 00:19:36,420 --> 00:19:41,610 So this means we cannot directly use the value stored in the targeting distance property in our module 281 00:19:41,610 --> 00:19:42,000 script. 282 00:19:42,000 --> 00:19:45,900 So instead, what I'm going to do is I'm going to create another variable. 283 00:19:45,900 --> 00:19:47,670 I'm going to call this targeting distance. 284 00:19:47,670 --> 00:19:51,240 And by default we're going to set it to properties dot targeting distance. 285 00:19:51,240 --> 00:19:56,100 But in any case when our zombie gets alerted while the zombie is wandering, we want to update this 286 00:19:56,100 --> 00:19:59,820 value to be equal to the alert targeting distance. 287 00:19:59,820 --> 00:20:05,730 And then we're going to use this variable instead to compare the distance between our zombie and the 288 00:20:05,730 --> 00:20:06,330 player. 289 00:20:07,280 --> 00:20:12,290 Now back inside of our while loop, we could go ahead and get this target from our get nearest player 290 00:20:12,290 --> 00:20:13,160 function. 291 00:20:13,160 --> 00:20:17,300 And here we would check if this function actually returned to us a target. 292 00:20:17,300 --> 00:20:20,870 If it did, then we can go ahead and pathfind towards this target. 293 00:20:20,870 --> 00:20:25,610 Otherwise, if we didn't get a target then this is where we would wander around the map and we could 294 00:20:25,610 --> 00:20:29,060 just print something like wander as a placeholder for now. 295 00:20:29,510 --> 00:20:35,330 Otherwise, if we did get a target, then we want to go ahead and update the walk speed of our humanoid 296 00:20:35,330 --> 00:20:41,900 to be equal to the chase speed, and then we can start generating a path for our zombie to start moving 297 00:20:41,900 --> 00:20:45,650 towards our player, so we can create another function for doing that. 298 00:20:45,980 --> 00:20:48,650 We'll call this create Path to. 299 00:20:48,650 --> 00:20:52,850 And here we will pass the target that we need to generate a path towards. 300 00:20:52,850 --> 00:20:57,110 So inside of this function we can go ahead and get a new path from our pathfinding service. 301 00:20:57,110 --> 00:21:02,420 And we're going to create path and pass the agent parameters that is inside of our properties. 302 00:21:02,600 --> 00:21:06,620 And then we can go ahead and use this path to compute a sync. 303 00:21:06,620 --> 00:21:09,440 The starting point is going to be my root path dot position. 304 00:21:09,440 --> 00:21:12,380 And the ending point is going to be the position of our target. 305 00:21:13,280 --> 00:21:18,950 And then we can go ahead and get all of the waypoints inside of this path. 306 00:21:21,120 --> 00:21:25,500 But the first thing we also want to check is whether or not this path was successfully generated. 307 00:21:25,530 --> 00:21:34,890 So if path dot status is not equal to the enum dot path status of success, then we need to get out 308 00:21:34,890 --> 00:21:42,060 of this function and instead have our zombie wander around the map so we could return path dot status. 309 00:21:42,420 --> 00:21:47,580 And since this function will return to us the status of our path, whether or not it was successful 310 00:21:47,580 --> 00:21:52,920 or unsuccessful, and what we could do down here is call our create path to function. 311 00:21:52,920 --> 00:21:54,540 And actually let me rename this. 312 00:21:54,540 --> 00:21:59,100 I'll do Ctrl shift R, I'll call it compute path to a target. 313 00:21:59,100 --> 00:22:04,380 So we're computing our path to our target that we got from our git nearest player function. 314 00:22:04,380 --> 00:22:10,680 And then we can go ahead and store inside of this success variable whether or not we successfully computed 315 00:22:10,680 --> 00:22:14,910 a path or if we didn't, if we did not successfully compute a path. 316 00:22:14,910 --> 00:22:23,760 So if let's say success is not equal to the enum dot path status of success, then we would also just 317 00:22:23,760 --> 00:22:25,110 wander here as well. 318 00:22:25,110 --> 00:22:28,860 And we could print something like wandering around the map. 319 00:22:28,860 --> 00:22:34,320 And when we are wandering, we would have to set the walk speed of our humanoid back to the default 320 00:22:34,320 --> 00:22:35,460 wandering speed. 321 00:22:35,460 --> 00:22:39,960 Otherwise when we see another target, then we'll update it back to the chasing speed. 322 00:22:40,610 --> 00:22:42,800 How we're back in our compute pass to function. 323 00:22:42,800 --> 00:22:47,870 If we were successful in generating a path, then we want to go ahead and start looping through all 324 00:22:47,870 --> 00:22:51,290 of the waypoints inside of our waypoints table. 325 00:22:51,740 --> 00:22:57,140 And this is where we would use the move two function to start moving our humanoid to each one of these 326 00:22:57,140 --> 00:22:57,740 waypoints. 327 00:22:57,740 --> 00:22:59,720 So we do waypoint dot position. 328 00:22:59,900 --> 00:23:06,620 And then if the action for this waypoint is equal to the enum dot path waypoint action dot jump, then 329 00:23:06,620 --> 00:23:11,630 we'll force our humanoid to jump, just like we did in that previous lecture where I talked about the 330 00:23:11,630 --> 00:23:12,860 pathfinding service. 331 00:23:13,880 --> 00:23:21,680 And then we'll make sure to wait for the My Humanoid Dot move to finished event to fire, and then we 332 00:23:21,680 --> 00:23:23,390 can move on to the next waypoint. 333 00:23:23,420 --> 00:23:28,220 However, we don't want to keep pathfinding if we can see the player. 334 00:23:28,220 --> 00:23:34,910 If the zombie is able to see the player instead of pathfinding, we can just have the zombie repeatedly 335 00:23:34,910 --> 00:23:41,690 run towards our player until we can't see the player anymore, or our zombie dies or the player dies. 336 00:23:42,200 --> 00:23:48,710 So that means we want to have the ability to have a function check to see if we are able to see this 337 00:23:48,710 --> 00:23:50,570 target we're chasing after. 338 00:23:50,570 --> 00:23:54,350 So we can have another function called check visibility. 339 00:23:54,560 --> 00:23:56,690 And we will pass the target to this function. 340 00:23:56,690 --> 00:24:00,830 And it could return to us a boolean of whether or not we can see this player. 341 00:24:00,830 --> 00:24:06,200 And then we could call this function within an if statement and check to see if we can see the target. 342 00:24:06,200 --> 00:24:08,270 And if we can see the target. 343 00:24:08,270 --> 00:24:13,640 Here we could have something like a repeat loop, where we would repeatedly move our zombie towards 344 00:24:13,640 --> 00:24:19,190 the position of our player until either the player died or we died, or we can't see them anymore, 345 00:24:19,190 --> 00:24:20,360 or stuff like that. 346 00:24:20,360 --> 00:24:26,900 So inside of our check visibility function, what we need to do is we need to go ahead and create a 347 00:24:26,900 --> 00:24:33,770 raycast from the head of our zombie to look towards the direction of where our target is, and then 348 00:24:33,770 --> 00:24:38,810 we're going to check if our raycast hit any of the limbs of our player, and if it did, that means 349 00:24:38,810 --> 00:24:40,190 we can see the player. 350 00:24:40,190 --> 00:24:43,970 So let's go ahead and create a new set of raycast parameters. 351 00:24:44,720 --> 00:24:49,100 And we're going to set the filter to send out instances to a table that contains our zombie, because 352 00:24:49,100 --> 00:24:50,780 we don't want the ray to hit our zombie. 353 00:24:50,810 --> 00:24:54,590 We also don't want our ray to hit any invisible parts within the workspace. 354 00:24:54,590 --> 00:24:57,350 So we're going to also include the filtering folder. 355 00:24:57,380 --> 00:25:03,830 And then lastly, we also don't want this zombies raycast to hit other zombies that may be on the map. 356 00:25:03,830 --> 00:25:08,180 So we're going to be parenting all of these zombies in our game to the zombies folder, which means 357 00:25:08,180 --> 00:25:14,330 we can go ahead and pass that folder to this table, and then we're going to ignore any zombies in our 358 00:25:14,330 --> 00:25:14,870 game. 359 00:25:15,410 --> 00:25:21,680 Then we can go ahead and update the filter type equal to the enum dot raycast filter type of exclude. 360 00:25:22,420 --> 00:25:29,200 And then let's go ahead and calculate the direction which is going to be the target position subtracted 361 00:25:29,200 --> 00:25:32,020 by the origin point, which is going to be our heads position. 362 00:25:32,170 --> 00:25:34,390 And then get the unit of that vector. 363 00:25:34,540 --> 00:25:38,560 And then we can go ahead and raycast from our head. 364 00:25:39,180 --> 00:25:40,950 In this direction. 365 00:25:41,070 --> 00:25:45,630 And then we want to multiply this direction by our targeting distance. 366 00:25:45,630 --> 00:25:51,240 So the length of our vector is going to be the same of how far the zombie can detect players. 367 00:25:51,240 --> 00:25:53,940 And then we're also going to pass our raycast parameters. 368 00:25:53,940 --> 00:25:57,090 Then we can go ahead and get the result from this raycast. 369 00:25:57,360 --> 00:25:59,700 And we can check whether or not we got a result. 370 00:25:59,700 --> 00:26:05,460 So if we did get a result and we want to make sure that this result belongs to a player's character 371 00:26:05,460 --> 00:26:06,000 model. 372 00:26:06,000 --> 00:26:12,150 So if the result instance is the descendant. 373 00:26:14,360 --> 00:26:15,200 Of. 374 00:26:15,200 --> 00:26:17,720 And then we're going to pass target dot parent. 375 00:26:17,720 --> 00:26:19,700 Then that means we can see a player. 376 00:26:19,700 --> 00:26:25,040 Because if this instance that we hit is like the limb of the player, let's say it's the leg, then 377 00:26:25,040 --> 00:26:30,200 it's going to definitely be a descendant of the target parent, which would be the player's character. 378 00:26:30,470 --> 00:26:38,480 However, we also need to include the property that we talked about earlier, which was our max visibility 379 00:26:38,480 --> 00:26:38,870 height. 380 00:26:38,870 --> 00:26:44,540 If you remember, if the player is high up in the sky, let's say they're standing on something. 381 00:26:44,540 --> 00:26:49,460 What we're going to technically say the zombie cannot see them because they're too high up. 382 00:26:49,460 --> 00:26:55,370 So this means we need to check the Y position of our player compared to the Y position of our zombie. 383 00:26:55,550 --> 00:26:58,550 So if the target. 384 00:26:59,340 --> 00:27:06,270 That position on the y axis, subtracted by the position of my zombie on the y axis. 385 00:27:06,270 --> 00:27:08,460 And then we want to get the absolute value of this. 386 00:27:08,460 --> 00:27:10,770 So we'll wrap it in math.abs. 387 00:27:10,770 --> 00:27:17,580 If this is greater than our properties dot max visibility height, then that tells us the player is 388 00:27:17,580 --> 00:27:24,690 too far up above the ground or above our zombie to be considered visible, so we're just going to return 389 00:27:24,690 --> 00:27:25,440 false. 390 00:27:25,440 --> 00:27:27,960 Otherwise we can go ahead and return true. 391 00:27:28,560 --> 00:27:34,890 Now if we didn't get a result from our raycast, or the result we got was not a descendant of a player's 392 00:27:34,890 --> 00:27:39,870 character or our target, then we're just going to go ahead and return false. 393 00:27:40,720 --> 00:27:46,420 So now what we can go ahead and do is if we check the visibility of our target and we can actually see 394 00:27:46,420 --> 00:27:53,260 them, then we can just repeatedly have our humanoid move to the position of our target. 395 00:27:53,260 --> 00:27:57,070 And I'm going to offset this position by the target. 396 00:27:57,100 --> 00:28:03,340 See frame dot look vector I'm going to multiply this by two studs to make the length two studs. 397 00:28:03,340 --> 00:28:04,570 Why am I doing this? 398 00:28:04,570 --> 00:28:10,030 Well, this allows our zombie to basically kind of predict where our player is going to move. 399 00:28:10,030 --> 00:28:15,580 You can imagine this position that we're passing right here to be two studs in front of the player. 400 00:28:15,580 --> 00:28:22,660 So this means the zombie is basically going to be trying to move ahead or towards the front of the player, 401 00:28:22,660 --> 00:28:27,100 which will make it look like the zombie is estimating where we're going to move, which looks kind of 402 00:28:27,100 --> 00:28:27,700 cool. 403 00:28:28,240 --> 00:28:34,780 And we can repeatedly do this and we'll have a yield statement in here for like 0.05 seconds. 404 00:28:34,780 --> 00:28:39,700 So we're going to constantly move our humanoid towards our player, and we're going to keep doing that 405 00:28:39,700 --> 00:28:42,700 until either we are no longer able to see the player. 406 00:28:42,700 --> 00:28:46,750 So we can call the check visibility function again and pass our target. 407 00:28:46,750 --> 00:28:49,540 And if we are not able to see the player. 408 00:28:50,670 --> 00:28:56,460 Or let's say we are no longer alive, or let's say our target parent. 409 00:28:56,460 --> 00:29:00,660 We get their humanoid and their health is less than or equal to zero. 410 00:29:00,660 --> 00:29:02,010 So our player is dead. 411 00:29:02,010 --> 00:29:05,130 Then we're going to end and break out of this repeat loop. 412 00:29:05,130 --> 00:29:10,890 And then once we're back in the for loop, since we can no longer see the player or we're not alive 413 00:29:10,890 --> 00:29:16,530 or the player is dead, then we want to completely break out of this for loop and go back into our main 414 00:29:16,530 --> 00:29:19,110 thread and try to find a new nearest player. 415 00:29:19,110 --> 00:29:24,210 So we'll just put a break statement here, and then I'm just going to print out something like. 416 00:29:25,110 --> 00:29:32,310 I am dead or can't see the player or the player is dead. 417 00:29:32,400 --> 00:29:36,240 Otherwise, there's a couple other conditions we want to check in this for loop. 418 00:29:36,240 --> 00:29:42,900 For example, we want to check the distance the target we're running after is away from our humanoid. 419 00:29:42,900 --> 00:29:47,250 So let's say the player is able to run and get far enough away from our zombie where they've gone beyond 420 00:29:47,250 --> 00:29:48,330 the targeting distance. 421 00:29:48,330 --> 00:29:49,740 Well, we need to check for that. 422 00:29:49,950 --> 00:29:55,350 So if my route part dot position, subtract it by the target's position. 423 00:29:55,620 --> 00:30:01,350 If the magnitude of that is greater than the targeting distance, then we need to break out of this 424 00:30:01,350 --> 00:30:08,310 for loop, stop pathfinding and find a new closest player because they have escaped our targeting distance. 425 00:30:08,310 --> 00:30:15,120 Another important thing we need to check is going to be the property of our recalculate path after distance. 426 00:30:15,120 --> 00:30:20,820 So if our zombie has been pathfinding for more than 20 studs and we still haven't seen the player, 427 00:30:20,820 --> 00:30:25,650 then the zombie needs to be like, okay, let me go ahead and see if there's a new closest player that 428 00:30:25,650 --> 00:30:31,230 I can pathfind towards, because I haven't seen this target that I'm trying to go after. 429 00:30:31,290 --> 00:30:39,150 So what we need to do is we need to check if the position of our route part, and we subtract this by 430 00:30:39,150 --> 00:30:41,100 the position of our first waypoint. 431 00:30:41,100 --> 00:30:45,210 So we'll do waypoints, get the first waypoint and then get that position. 432 00:30:45,210 --> 00:30:53,130 If the magnitude between those two positions is greater than our properties dot recalculate path after 433 00:30:53,130 --> 00:31:01,260 distance, then we're also going to break out of this for loop because here this means we've been pathfinding 434 00:31:01,260 --> 00:31:04,920 for too long and didn't see the player. 435 00:31:05,620 --> 00:31:12,550 And then remember here, this is where the player is too far away from our zombie. 436 00:31:13,180 --> 00:31:19,480 And then once we're out of this for loop, we still want to return what the status of our path was. 437 00:31:19,480 --> 00:31:22,030 So we'll make sure to return path dot status. 438 00:31:22,030 --> 00:31:29,350 And that way, if we were successfully generating a path, we can store that inside of our success variable. 439 00:31:29,350 --> 00:31:34,030 And that way we do not start having the zombie wandering around the map, and instead our zombie is 440 00:31:34,030 --> 00:31:36,220 going to go and try to find a new target. 441 00:31:36,220 --> 00:31:40,600 And if we do find that target, then we're going to chase after that particular target. 442 00:31:41,380 --> 00:31:46,660 Now, another important thing we need to do in this main section is we need to apply all of those humanoid 443 00:31:46,660 --> 00:31:51,460 stats that we defined within our properties module script, and that's going to be very easy to do. 444 00:31:51,490 --> 00:31:57,280 We can just go ahead and loop through every single property and its value inside of our properties, 445 00:31:57,280 --> 00:31:58,930 dot humanoid stats. 446 00:31:59,170 --> 00:32:02,650 And then on our humanoid we can go ahead and set that property. 447 00:32:03,760 --> 00:32:06,340 To be equal to that value. 448 00:32:07,490 --> 00:32:11,510 Okay, let's go ahead and test out what we've gotten written out so far. 449 00:32:11,600 --> 00:32:18,260 And I'm also going to go ahead and generate a bunch of parts on the workspace so we can see the path 450 00:32:18,260 --> 00:32:21,320 that is generated by our compute path two function. 451 00:32:21,320 --> 00:32:26,900 So what I'm going to do is I'm going to loop through every single waypoint in waypoints. 452 00:32:26,900 --> 00:32:28,880 And we're going to create a new part. 453 00:32:30,910 --> 00:32:34,540 And we can go ahead and set the shape equal to ball. 454 00:32:34,540 --> 00:32:38,200 So enum dot part type dot ball. 455 00:32:39,570 --> 00:32:45,450 We'll set the material equal to the enum dot material of neon. 456 00:32:47,250 --> 00:32:49,800 We'll set the part's color equal to. 457 00:32:49,800 --> 00:32:51,960 Actually let's go ahead and generate a random color. 458 00:32:52,260 --> 00:32:57,210 So I'll create a variable called color that's going to be equal to color three from RGB. 459 00:32:57,540 --> 00:33:00,090 Or actually we could do dot new. 460 00:33:00,090 --> 00:33:06,750 And then we're just going to pass math dot random and then math dot random and then math dot random. 461 00:33:09,210 --> 00:33:12,990 And then we can go ahead and set the color of our part equal to this new color. 462 00:33:13,590 --> 00:33:18,060 So that way every single path that the AI generates is going to be a different color. 463 00:33:18,060 --> 00:33:21,690 And then we can set the size equal to a new vector three dot new. 464 00:33:21,690 --> 00:33:24,450 And we'll just do like half a stud in size. 465 00:33:24,810 --> 00:33:28,980 We'll set the position of the part equal to this waypoints position. 466 00:33:30,790 --> 00:33:32,830 We'll set anchored equal to true. 467 00:33:32,830 --> 00:33:35,710 We'll set can collide equal to false. 468 00:33:35,710 --> 00:33:38,980 And then we'll make sure to parent this into the workspace. 469 00:33:38,980 --> 00:33:42,850 So that way we are able to visualize the path of our zombie generates. 470 00:33:43,360 --> 00:33:47,470 So now we can go ahead and keep our zombie over here. 471 00:33:47,800 --> 00:33:54,580 And actually one last very important thing we need to do is we actually need to ensure that we are yielding 472 00:33:54,580 --> 00:33:55,750 in this while loop. 473 00:33:55,840 --> 00:33:58,930 So we need to make sure to put a yield statement down here. 474 00:33:58,930 --> 00:34:00,880 And we could wait for like a 10th of a second. 475 00:34:00,880 --> 00:34:02,350 So we'll do 0.1 seconds. 476 00:34:02,350 --> 00:34:07,180 That way we don't accidentally crash our game because we don't want to have a while loop that's repeatedly 477 00:34:07,180 --> 00:34:08,680 executing without a yield statement. 478 00:34:08,680 --> 00:34:12,760 So we make sure we'll yield and then we can go ahead and play test our game. 479 00:34:14,530 --> 00:34:19,360 As you can see, it already is being printed out in the console that our zombie is quote unquote wandering 480 00:34:19,360 --> 00:34:20,830 because there's no player nearby. 481 00:34:20,830 --> 00:34:25,060 But now that I'm on the map, if I get close to my zombie. 482 00:34:26,280 --> 00:34:27,510 He isn't chasing me. 483 00:34:27,510 --> 00:34:30,120 He's still wandering around the map for some reason. 484 00:34:31,570 --> 00:34:34,000 So he's not detecting that I am nearby. 485 00:34:34,000 --> 00:34:37,480 And let's go ahead and see why that may be the case. 486 00:34:38,310 --> 00:34:42,210 So we should be getting a target here from our get nearest player function. 487 00:34:42,210 --> 00:34:44,880 If we actually go to the get nearest player function. 488 00:34:45,900 --> 00:34:50,190 The reason is we forgot to put a Not statement here. 489 00:34:50,190 --> 00:34:54,210 So we need to make sure that this player does not have a character and we're skipping them. 490 00:34:54,210 --> 00:34:56,340 Not that they do have a character. 491 00:34:56,340 --> 00:34:58,470 So we'll make sure to put that not statement here. 492 00:34:58,710 --> 00:35:00,990 And now we can go ahead and put this the game again. 493 00:35:02,510 --> 00:35:04,070 And it says our zombie is wandering. 494 00:35:04,070 --> 00:35:05,900 And then if we spawn into the map. 495 00:35:06,470 --> 00:35:11,840 As you can see, it stopped wandering and now you can see the this point was generated. 496 00:35:11,840 --> 00:35:13,130 Oh, there's another path. 497 00:35:13,640 --> 00:35:16,940 And our zombie is now starting to walk towards us. 498 00:35:16,940 --> 00:35:19,550 As you can see, our zombie isn't walking along that path. 499 00:35:19,550 --> 00:35:26,690 It generated because it can see our player, and instead our repeat loop is forcing our zombie to continually 500 00:35:26,690 --> 00:35:28,430 chase after our player. 501 00:35:28,430 --> 00:35:34,670 But once I break the line of sight with my zombie, my zombie should generate a new path to try and 502 00:35:34,670 --> 00:35:35,240 get after me. 503 00:35:35,240 --> 00:35:39,440 So, for example, if I run all the way down here and get behind this pillar. 504 00:35:40,110 --> 00:35:44,880 As you can see, the zombie now has to pathfind to get towards me. 505 00:35:44,880 --> 00:35:50,190 And now that he sees me again, he's coming towards me once more and he's chasing after me. 506 00:35:50,900 --> 00:35:52,820 If I go around this corner once again. 507 00:35:52,820 --> 00:35:55,370 As you can see, the zombie has lost sight of me. 508 00:35:56,770 --> 00:36:00,850 And we are running into an issue where our zombie is stuck. 509 00:36:00,850 --> 00:36:02,170 Okay, now he's moving. 510 00:36:02,500 --> 00:36:05,380 We're going to address that problem we just saw in a little bit. 511 00:36:05,740 --> 00:36:12,070 But you may be seeing that the zombie is being how could I say it very slow when pathfinding. 512 00:36:12,430 --> 00:36:14,170 So again he's frozen again. 513 00:36:16,820 --> 00:36:19,220 And then we're going to be waiting. 514 00:36:19,220 --> 00:36:19,520 Okay. 515 00:36:19,520 --> 00:36:25,250 You can see as he moves to each one of these points, he's kind of like stopping, starting, stopping, 516 00:36:25,250 --> 00:36:25,820 starting. 517 00:36:25,820 --> 00:36:30,200 That's because I am recording and hosting this server on my computer at the same time. 518 00:36:30,200 --> 00:36:34,400 So the server is being a little bit slow with, uh, my I zombie. 519 00:36:34,400 --> 00:36:37,850 If I wasn't recording then the pathfinding would look very smooth. 520 00:36:37,850 --> 00:36:41,930 That's just an artifact of me recording and doing this at the same time. 521 00:36:41,930 --> 00:36:48,650 But let's go ahead and address that problem where our zombie seems to freeze in place before he starts 522 00:36:48,650 --> 00:36:49,880 pathfinding again. 523 00:36:49,880 --> 00:36:55,670 And the reason this is occurring is because if we go. 524 00:36:57,290 --> 00:36:58,010 To. 525 00:36:58,010 --> 00:37:05,060 I believe when we're waiting for this event right here, sometimes our humanoid may not be able to move 526 00:37:05,060 --> 00:37:11,360 to a particular waypoint for some reason, and this event will be automatically fired after a certain 527 00:37:11,360 --> 00:37:14,330 period of time if our zombie doesn't move to a position. 528 00:37:14,330 --> 00:37:19,370 But we do not want to wait that long for our zombie to be stuck there. 529 00:37:19,370 --> 00:37:20,390 Sitting there. 530 00:37:20,390 --> 00:37:26,180 So what we want to do instead is we want to be able to basically have a time out here where we're either 531 00:37:26,180 --> 00:37:31,370 going to wait for this event to fire, or we're going to wait for a specific amount of time to pass. 532 00:37:31,970 --> 00:37:38,870 Now, unfortunately, Roblox does not have an inbuilt feature to this wait function where we can supply 533 00:37:38,870 --> 00:37:45,170 a number to act as a timeout, which means we're going to have to implement it ourselves. 534 00:37:45,170 --> 00:37:50,690 And actually, it's not too complicated to implement a function to either wait for an event or wait 535 00:37:50,690 --> 00:37:52,070 for a timeout to happen. 536 00:37:52,070 --> 00:37:56,840 And we're going to be using coroutines to create this function. 537 00:37:56,840 --> 00:38:00,920 So what I'm going to do is at the top, I'm going to create a new function. 538 00:38:00,920 --> 00:38:04,400 And I'm going to call this wait with timeout. 539 00:38:04,400 --> 00:38:10,160 And what we're going to pass to this function is an event which would be an RPC script signal and a 540 00:38:10,160 --> 00:38:12,260 number which would be our timeout. 541 00:38:12,410 --> 00:38:17,600 Now what we're going to do in this function is we're going to grab the current executing thread inside 542 00:38:17,600 --> 00:38:18,560 of this function. 543 00:38:18,560 --> 00:38:20,750 And then we're going to yield that thread. 544 00:38:20,750 --> 00:38:23,510 And we're only going to resume the thread once. 545 00:38:23,510 --> 00:38:27,830 Either this event fires or this timeout is reached. 546 00:38:27,860 --> 00:38:34,100 Now to get the current thread that is executing within this function we can use the coroutine dot running 547 00:38:34,100 --> 00:38:34,610 function. 548 00:38:34,610 --> 00:38:36,920 And it says returns the running coroutine. 549 00:38:36,920 --> 00:38:41,810 So we can get the current executing thread and store it in a variable like that. 550 00:38:41,810 --> 00:38:45,320 So this is the current executing thread that is going through this function. 551 00:38:45,650 --> 00:38:50,480 And then what we want to do is we want to connect a function to this event. 552 00:38:50,480 --> 00:38:53,900 And I'm going to type annotate this as an RPC script signal. 553 00:38:53,900 --> 00:38:58,790 You don't need to worry as always about type annotation, I'm just doing it for the sake of this lecture. 554 00:38:59,030 --> 00:39:02,900 But that way we can actually see the different functions for our event. 555 00:39:03,470 --> 00:39:09,200 And we're going to connect a function once to this event, pass our function here. 556 00:39:09,200 --> 00:39:15,500 And then we're also going to do something at the same time, which is we're going to delay a function 557 00:39:15,500 --> 00:39:17,030 from running. 558 00:39:17,180 --> 00:39:21,740 And we're going to delay it equal to the timeout that is passed to our function. 559 00:39:23,820 --> 00:39:29,430 And now this means either this function right here is going to execute first, or this function right 560 00:39:29,430 --> 00:39:31,110 here is going to execute first. 561 00:39:31,110 --> 00:39:37,320 Whichever one executes first, their purpose is going to be resuming this thread, allowing the main 562 00:39:37,320 --> 00:39:40,530 thread of execution to come back to this function and return out of it. 563 00:39:40,530 --> 00:39:47,340 And we should be able to return a true or false boolean to let us know whether or not we timed out or 564 00:39:47,340 --> 00:39:48,510 the event fired. 565 00:39:49,350 --> 00:39:54,960 Now, in order to yield the thread inside of this function, we can return. 566 00:39:54,960 --> 00:40:00,840 And then we're going to call the coroutine dot yield function to suspend the execution of our thread. 567 00:40:01,140 --> 00:40:06,000 So when the main thread of execution goes into this function, it's going to set all of this up. 568 00:40:06,000 --> 00:40:07,650 And then it's going to hit this statement right here. 569 00:40:07,650 --> 00:40:11,700 And it's going to pause or freeze inside of our function. 570 00:40:11,700 --> 00:40:17,820 And it's only going to come back to this point and return out of our function once we actually resume 571 00:40:17,820 --> 00:40:18,720 the thread. 572 00:40:19,020 --> 00:40:24,990 And we can do that either within our task dot delay function or the function connected to our event. 573 00:40:25,640 --> 00:40:30,950 So, for example, what we need to do is we actually need to store the connection. 574 00:40:31,940 --> 00:40:37,190 To this event, and this is how we're going to keep track of whether or not this function executed or 575 00:40:37,190 --> 00:40:38,630 this function executed. 576 00:40:38,660 --> 00:40:43,010 Let's say we reach the timeout before this event ever executes. 577 00:40:43,010 --> 00:40:48,710 Well, what we want to go ahead and do is we want to disconnect the connection, and then we want to 578 00:40:48,710 --> 00:40:50,300 set connection equal to nil. 579 00:40:50,300 --> 00:40:55,250 And this is how we're going to keep track if one of these functions executed first. 580 00:40:55,250 --> 00:41:00,620 Because let's say this function executed and then set connection equal to nil. 581 00:41:00,620 --> 00:41:07,010 And then this function executed well we can check in this function if connection is equal to nil. 582 00:41:07,010 --> 00:41:10,730 If it is that means this function up here already executed. 583 00:41:10,730 --> 00:41:13,610 And we can go ahead and return and do nothing. 584 00:41:14,000 --> 00:41:16,400 And we could go ahead and do the same thing here as well. 585 00:41:16,400 --> 00:41:21,740 If this function executed before this function does, we can check if connection is equal to nil. 586 00:41:21,740 --> 00:41:24,410 And if it is then we're also going to return. 587 00:41:24,410 --> 00:41:28,160 Otherwise inside of this one we can go ahead and set connection equal to nil. 588 00:41:28,160 --> 00:41:34,070 I don't need to disconnect the connection right here because we are using the once function, which 589 00:41:34,070 --> 00:41:36,170 will automatically disconnect it for us. 590 00:41:36,830 --> 00:41:42,290 But after either this function executes first or this function executes first, we can go ahead and 591 00:41:42,290 --> 00:41:48,440 use the task dot spawn function to resume our thread, because we can pass a function or a thread here, 592 00:41:48,440 --> 00:41:51,740 and this is where we're going to pass our thread to resume it. 593 00:41:51,740 --> 00:41:57,500 And then other additional information we're going to pass here is going to be a boolean of true or false, 594 00:41:57,710 --> 00:41:59,570 since I want this to return a boolean. 595 00:41:59,570 --> 00:42:04,760 If it timed out, then if this function right here executed, then that means yes, we did time out 596 00:42:04,760 --> 00:42:06,380 so we can return true. 597 00:42:06,380 --> 00:42:11,900 Otherwise we can copy this and resume the thread here and pass false instead. 598 00:42:11,900 --> 00:42:18,620 Meaning no, we did not time out the event fired and then since this event might pass additional information, 599 00:42:18,620 --> 00:42:22,460 I'll make sure to also pass that to my task dot spawn function. 600 00:42:22,460 --> 00:42:29,300 But now we have created a simple function that's either going to wait for our event to fire, or it's 601 00:42:29,300 --> 00:42:33,680 going to wait for our timeout to reach, and then it's going to return to us a true or false buoyant 602 00:42:33,680 --> 00:42:35,780 to let us know whether or not we timed out. 603 00:42:36,630 --> 00:42:43,500 Now we can go down to our Compute path two function, and instead of using the wait function to wait 604 00:42:43,500 --> 00:42:48,270 for the move to finish event, we can instead call our wait with timeout function. 605 00:42:48,270 --> 00:42:53,070 Pass my humanoid dot move to finished and then we could pass a timeout. 606 00:42:53,070 --> 00:42:59,940 We wish to wait and we could do something like maybe three seconds, and then we can store in a variable 607 00:42:59,940 --> 00:43:01,530 whether or not we timed out. 608 00:43:01,530 --> 00:43:03,180 So I'll just call this timed out. 609 00:43:04,230 --> 00:43:07,500 And then we can go ahead and check to see if we timed out. 610 00:43:07,500 --> 00:43:15,510 If we timed out, then that means our humanoid did not make it to our waypoint within this time threshold. 611 00:43:15,510 --> 00:43:19,020 And that could indicate that our humanoid is stuck. 612 00:43:19,020 --> 00:43:25,590 So I'll print out a warning and say zombie timed out and we could ask something like is it stuck? 613 00:43:26,250 --> 00:43:28,950 And what I'm going to do is just in case. 614 00:43:28,950 --> 00:43:32,190 Maybe my zombie is, like, stuck in a little divot or something. 615 00:43:32,190 --> 00:43:35,310 I'll force my humanoid to jump. 616 00:43:36,880 --> 00:43:41,530 And then we're going to compute a path to our target and try again. 617 00:43:41,530 --> 00:43:43,000 And we're going to break. 618 00:43:43,520 --> 00:43:45,320 Out of our for loop. 619 00:43:45,320 --> 00:43:49,820 And actually what we could do instead is, since we're calling the compute path two function within 620 00:43:49,820 --> 00:43:56,210 the compute path two function, which is a recursive function call, we can go ahead and store the result 621 00:43:56,210 --> 00:43:57,890 from our compute path two function. 622 00:43:57,890 --> 00:44:03,020 And instead of breaking here, we'll just return that result when we break out of this function right 623 00:44:03,020 --> 00:44:03,530 here. 624 00:44:04,070 --> 00:44:09,680 So we're only going to recompute the path just in case our zombie gets stuck. 625 00:44:09,680 --> 00:44:13,580 And it has to generate a new path to a new closest player. 626 00:44:15,160 --> 00:44:18,070 So now we can go ahead and playtest our game again. 627 00:44:18,740 --> 00:44:22,640 And as you can see, it says our zombie is quote unquote wandering. 628 00:44:22,640 --> 00:44:26,720 And then once we spawn into the map, we are now within the targeting range for our zombie. 629 00:44:26,840 --> 00:44:32,090 As you can see, our zombie is trying to run directly towards us because the raycast is hitting our 630 00:44:32,090 --> 00:44:33,680 player through those bars. 631 00:44:33,680 --> 00:44:39,530 But as soon as I break the line of sight of that raycast, as you can see, our zombie generated a new 632 00:44:39,530 --> 00:44:40,340 path. 633 00:44:40,580 --> 00:44:43,310 But since our zombie can see me, he's chasing after me. 634 00:44:43,310 --> 00:44:45,740 And then if I break and lose sight of him. 635 00:44:45,890 --> 00:44:51,770 As you can see now he is pathfinding to get towards us and he's continuing to generate a new path. 636 00:44:53,780 --> 00:44:57,230 So he's continuing to generate his path to generating a path. 637 00:44:58,150 --> 00:45:04,210 So as you can see, since my zombie is going along this path more than 20 studs, he is recomputing 638 00:45:04,210 --> 00:45:11,350 the path to make sure that there isn't a new closest player nearby, and it's of course, grabbing us 639 00:45:11,350 --> 00:45:13,480 instead because we're the only player in the game. 640 00:45:13,720 --> 00:45:19,510 But once he path finds for more than 20 studs, he's going to recompute the path again. 641 00:45:19,510 --> 00:45:22,270 As you can see, he computes it again and then he should. 642 00:45:22,300 --> 00:45:22,840 There we go. 643 00:45:22,840 --> 00:45:27,850 He computed another path, and that's because he's reaching that threshold where the zombie is like, 644 00:45:27,850 --> 00:45:31,600 okay, I've been pathfinding for more than 20 studs and I don't see the player. 645 00:45:31,780 --> 00:45:34,960 Of course, you can go ahead and modify this value to be larger. 646 00:45:34,960 --> 00:45:41,800 Like maybe you want to set it to 30 or 40 studs, but I found a value of 20 studs to be decent enough 647 00:45:41,800 --> 00:45:47,500 to where our zombie is going to pathfind towards us, and then once he path finds for too long, he 648 00:45:47,500 --> 00:45:49,390 should recompute a new path. 649 00:45:49,390 --> 00:45:52,030 As you can see, he just recomputed a new path again. 650 00:45:52,030 --> 00:45:58,780 He recomputed another one and now he sees us and he's chasing after us and that's pretty cool. 651 00:45:59,410 --> 00:46:04,600 Okay, so let's go through a quick recap of the code we've just written, just to make sure you understand 652 00:46:04,600 --> 00:46:06,070 everything that is going on. 653 00:46:06,070 --> 00:46:12,220 So we have a while loop at the bottom of our code that's going to constantly check for the closest player 654 00:46:12,220 --> 00:46:13,330 to our zombie. 655 00:46:13,330 --> 00:46:18,760 So this get nearest player function goes through all the players in our game checks the distances between 656 00:46:18,760 --> 00:46:22,600 all those players, and then it returns to us the closest target to us. 657 00:46:23,020 --> 00:46:28,450 If this function returns a target, then we're going to go ahead and compute a path towards that target 658 00:46:28,450 --> 00:46:30,040 and start chasing after them. 659 00:46:30,040 --> 00:46:35,350 Otherwise, if we did not find a target, we're going to wander around the map instead. 660 00:46:35,350 --> 00:46:42,010 Now, inside of our compute path two function, what we're doing is we are generating a new path using 661 00:46:42,010 --> 00:46:43,330 the pathfinding service. 662 00:46:43,330 --> 00:46:48,070 And then we're checking to make sure that we were successful in generating that path to this particular 663 00:46:48,070 --> 00:46:48,670 player. 664 00:46:49,270 --> 00:46:53,110 If we were not successful, then that means our zombie is going to wander around the map. 665 00:46:53,110 --> 00:46:57,880 Otherwise we start moving our zombie along the different waypoint positions. 666 00:46:58,180 --> 00:47:04,150 If our zombie is able to see our player, then instead our zombie is going to continually run after 667 00:47:04,150 --> 00:47:08,830 our player until the zombie is no longer able to see them, or our zombie dies. 668 00:47:08,830 --> 00:47:09,880 Or. 669 00:47:10,460 --> 00:47:12,350 The health of our humanoid. 670 00:47:12,380 --> 00:47:15,350 The player we're chasing after hits zero. 671 00:47:15,440 --> 00:47:21,590 We're also checking if the player gets out of range of the targeting distance for our zombie, and we're 672 00:47:21,590 --> 00:47:25,160 also checking to see if we've been pathfinding for more than 20 studs. 673 00:47:25,160 --> 00:47:29,690 And if we are, we're going to recompute a new path by breaking out of this for loop. 674 00:47:30,050 --> 00:47:37,550 Now, if for some reason the move two finished event for our humanoid does not fire within our timeout, 675 00:47:37,550 --> 00:47:40,310 then that could possibly mean that our zombie is stuck. 676 00:47:40,310 --> 00:47:45,320 So what we're going to do is we're going to force our zombie to jump, and then we're going to recompute 677 00:47:45,320 --> 00:47:51,350 a new path to our target to see if it is possible to get back on track with our target. 678 00:47:51,350 --> 00:47:59,600 And then we'll return that result here out of our function, which means it'll go back to the while 679 00:47:59,600 --> 00:48:01,820 loop and store that in this variable. 680 00:48:01,820 --> 00:48:04,220 And then we can check whether or not we were successful. 681 00:48:04,220 --> 00:48:05,990 If we weren't, we'll wander around the map. 682 00:48:05,990 --> 00:48:11,630 Otherwise we'll go through the next iteration of this while loop, find a new target, and start chasing 683 00:48:11,630 --> 00:48:14,780 after that player, starting the entire loop all over again. 684 00:48:14,780 --> 00:48:21,260 So this is just a constant loop of our zombie trying to find any players that are close to us. 685 00:48:22,070 --> 00:48:28,370 Now of course, another thing I want to go ahead and add is I want to update the alive variable if our 686 00:48:28,370 --> 00:48:29,510 zombie dies. 687 00:48:29,510 --> 00:48:31,040 And that's very easy to do. 688 00:48:31,040 --> 00:48:38,690 If my humanoid and the died event fires, then we can connect a function set alive equal to false. 689 00:48:39,200 --> 00:48:45,470 And then we could wait for a small period of time, like five seconds, and then we'll simply just destroy 690 00:48:45,470 --> 00:48:48,650 our zombie off the map because our zombie is dead. 691 00:48:49,740 --> 00:48:54,870 And actually, another very important thing we need to do for our zombie is that since the server is 692 00:48:54,870 --> 00:49:00,720 controlling the pathfinding for our zombie, we also want the server to have network ownership over 693 00:49:00,720 --> 00:49:04,650 our zombie, and we don't want players to have a network ownership over our zombies. 694 00:49:04,650 --> 00:49:11,190 So inside of the main section, another thing we can do is we can refer to our root part and we'll set 695 00:49:11,190 --> 00:49:18,150 the network owner equal to nil, which will automatically set the network owner equal to the server. 696 00:49:18,150 --> 00:49:21,750 So now the server has network ownership over our zombie. 697 00:49:21,750 --> 00:49:27,210 No player is ever going to get network ownership back over our zombie unless the server sets network 698 00:49:27,210 --> 00:49:31,590 ownership back to be automatic, and we can go ahead and do that when our zombie dies. 699 00:49:31,590 --> 00:49:35,550 Because when our zombie dies, we don't care about the zombie anymore. 700 00:49:35,550 --> 00:49:40,920 So we can set the network ownership back to automatic for our root part. 701 00:49:41,340 --> 00:49:46,710 Now, another thing I want to actually listen for is I want to listen for when the health changes within 702 00:49:46,710 --> 00:49:47,520 my humanoid. 703 00:49:47,520 --> 00:49:49,950 So we're going to listen to the health changed event. 704 00:49:50,520 --> 00:49:53,580 We're going to connect and get a function. 705 00:49:53,580 --> 00:49:58,770 And this function is going to pass to us the new health that was set on our humanoid. 706 00:49:59,400 --> 00:50:03,270 And we want to see if this health is less than our zombies previous health. 707 00:50:03,270 --> 00:50:09,000 If it is, then that means our zombie got hurt by something which is probably a player, and that means 708 00:50:09,000 --> 00:50:14,490 we want to update our targeting distance variable to be equal to that properties dot alert targeting 709 00:50:14,490 --> 00:50:15,450 distance. 710 00:50:15,540 --> 00:50:19,140 So we can go ahead and keep track of the zombies previous health. 711 00:50:19,700 --> 00:50:24,410 With any variable, we'll call it current health. 712 00:50:25,010 --> 00:50:29,390 And by default we'll set that to properties dot humanoid stats dot health. 713 00:50:30,060 --> 00:50:36,030 And back inside of our event listener, we can go ahead and check to see if the new health that our 714 00:50:36,030 --> 00:50:42,690 humanoid was set to is less than our current health, which means our zombie got damaged. 715 00:50:42,690 --> 00:50:47,340 So if our zombie got damaged, we're going to update the targeting distance to be equal to the alert 716 00:50:47,370 --> 00:50:48,750 targeting distance. 717 00:50:49,170 --> 00:50:53,790 And then we'll also make sure to set current health to be equal to this new health. 718 00:50:54,380 --> 00:50:58,160 Okay, so we've got a lot done for our zombie. 719 00:50:58,160 --> 00:51:00,410 And this lecture is getting a little bit long. 720 00:51:00,410 --> 00:51:02,780 So we're going to continue scripting our AI zombie. 721 00:51:02,780 --> 00:51:08,030 And the next lecture specifically we're going to be adding the different sounds for our zombies. 722 00:51:08,030 --> 00:51:13,370 We're going to be allowing our zombie to wander around when it can't find any nearby players. 723 00:51:13,370 --> 00:51:17,150 We're going to be adding health regeneration as well as a few more things. 724 00:51:17,150 --> 00:51:19,850 So I will see you in the next lecture.